//===============================================================================
// Microsoft patterns & practices
// Guidance Explorer Offline Client
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using GuidanceExplorer.Controls;
using GuidanceExplorer.Model;
using GuidanceExplorer.Services;

namespace GuidanceExplorer.Views
{
    /// <summary>
    /// Display the contents of the currently selected node.
    /// </summary>
    public partial class GuidanceItemListView : UserControl, IMessageFilter
    {
        #region Public Events

        public event EventHandler<GuidanceItemEventArgs> GuidanceItemSelected;

        public event EventHandler<GuidanceItemCollectionEventArgs> GuidanceItemsDeleted;

        public event EventHandler<GuidanceItemFileReferenceEventArgs> GuidanceItemOpened;

        public event EventHandler<CancelEventArgs> OpeningContextMenuListView;

        #endregion

        #region Private Members

        /// <summary>
        /// Keep the reference to the storage service.
        /// </summary>
        private IModelStorageService _modelService;

        /// <summary>
        /// Used to notify the number of rows in the list.
        /// </summary>
        private ToolStripStatusLabel _statusLabel;
        
        /// <summary>
        /// True if GE is sync'ing with online store. Impacts status label.
        /// </summary>
        private bool _synchronizing;

        /// <summary>
        /// The items collection that is being displayed.
        /// </summary>
        private IGuidanceItemContainer _itemsContainer;

        /// <summary>
        /// The handle to the header.
        /// </summary>
        private IntPtr _headerhWnd;

        /// <summary>
        /// The current column collection
        /// </summary>
        private ColumnCollection _columns;

        #endregion

        #region Service Dependencies

        public IModelStorageService ModelService
        {
            set { _modelService = value; }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Default constructor.
        /// </summary>
        public GuidanceItemListView()
        {
            InitializeComponent();

            Application.AddMessageFilter(this);
            //Get the handle to the header of the list.
            _headerhWnd = Win32.SendMessage(list.Handle, Win32.LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
            Win32.SendMessage(_headerhWnd, Win32.HDM_SETIMAGELIST, IntPtr.Zero, imageList.Handle);
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// True if GE is sync'ing. Impacts the toolbar status label
        /// </summary>
        public bool IsSynchronizing
        {
            set { _synchronizing = value; }
        }

        /// <summary>
        /// Used to notify the number of rows in the list.
        /// </summary>
        public ToolStripStatusLabel StatusLabel
        {
            set { _statusLabel = value; }
        }

        public IGuidanceItemContainer Items
        {
            get { return _itemsContainer; }
        }

        #endregion

        /// <summary>
        /// Public operation called by the tree view for displaying contents of a container.
        /// </summary>
        /// <param name="itemsContainer">The items container instance.</param>
        public void ShowContents(IGuidanceItemContainer itemsContainer, ColumnCollection columnCollection)
        {
            try
            {
                list.BeginUpdate();

                //Remove the items from the list.
                list.Items.Clear();

                //Remove the sorting of the list.
                list.ListViewItemSorter = null;

                //Use default column structure.
                if (itemsContainer.Columns == null || itemsContainer.Columns.Count == 0)
                {
                    if (columnCollection == null || columnCollection.Count == 0)
                    {
                        _columns = itemsContainer.Columns = ColumnCollection.DefaultColumns(GuidanceType.New(GuidanceType.DefaultTypeName));
                    }
                    else
                    {
                        _columns = columnCollection;
                    }
                }
                else
                {
                    _columns = itemsContainer.Columns;
                }

                //Prepare the columns in the list.
                PrepareColumns(_columns);

                //Keep the enumerator.
                _itemsContainer = itemsContainer;

                //Populate the list.
                PopulateList(itemsContainer, _columns);

                //Set the sorter
                list.ListViewItemSorter = new GuidanceItemListViewComparer(_columns);
            }
            catch (Exception e)
            {
                string s = e.Message;
            }
            finally
            {
                list.EndUpdate();
            }

            if (_statusLabel != null)
            {
                if (!_synchronizing)
                {
                    _statusLabel.Text = string.Format("{0} items", list.Items.Count);
                }
            }
        }

        /// <summary>
        /// Return the currently selected guidance item.
        /// </summary>
        /// <returns></returns>
        public GuidanceItem GetCurrentGuidanceItem()
        {
            //If a single item is selected
            if (list.SelectedItems.Count >= 1)
            {
                GuidanceItemListViewItem listViewItem = list.SelectedItems[0] as GuidanceItemListViewItem;
                if (listViewItem != null)
                {
                    return listViewItem.GetGuidanceItemFileReference(_modelService.Guidance);
                }
                else
                {
                    throw new NotSupportedException();
                }
            }
            else
            {
                //If no items are selected.
            }
            return null;
        }

        /// <summary>
        /// Return all the selected guidance items.
        /// </summary>
        /// <returns></returns>
        public GuidanceItem[] GetSelectedGuidanceItems()
        {
            int count = list.SelectedItems.Count;

            if (count >= 1)
            {
                List<GuidanceItem> items = new List<GuidanceItem>(count);

                for (int i = 0; i < count; i++)
                {
                    GuidanceItemListViewItem listViewItem = list.SelectedItems[i] as GuidanceItemListViewItem;
                    if (listViewItem == null)
                    {
                        continue;
                    }

                    items.Add(listViewItem.GuidanceItem);
                }

                return items.ToArray();
            }
            else
            {
                //If no items are selected.
            }
            return null;
        }

        /// <summary>
        /// Select all the GuidanceItems in the list view
        /// </summary>
        public void SelectAllItems()
        {
            list.SelectedIndices.Clear();

            for (int i = 0; i < list.Items.Count; i++)
            {
                list.SelectedIndices.Add(i);
            }
        }

        /// <summary>
        /// Refresh the contents by loading the content into the list.
        /// </summary>
        /// <param name="item"></param>
        public void RefreshItems(GuidanceItem item)
        {
            list.Items.Clear();
            PopulateList(_itemsContainer, _itemsContainer.Columns);

            list.SelectedItems.Clear();

            if (list.Items.ContainsKey(item.Id))
            {
                SelectItem(item.Id);
            }
        }

        public void SelectSingleItem(string id)
        {
            list.SelectedIndices.Clear();
            SelectItem(id);
        }

        public void SelectItem(string id)
        {
            if (list.Items.ContainsKey(id))
            {
                GuidanceItemListViewItem listItem = list.Items[id] as GuidanceItemListViewItem;
                if (listItem != null)
                {
                    listItem.Selected = true;
                    listItem.Focused = true;
                    list.EnsureVisible(listItem.Index);
                    list.SelectedIndices.Add(listItem.Index);

                    GuidanceItemSelected(this, new GuidanceItemEventArgs(listItem.GuidanceItem));
                }
            }
        }

        /// <summary>
        /// Refresh the data in the list view.
        /// </summary>
        private void RefreshData()
        {
            //Recalculate the list?
            GuidanceItemListViewComparer oldComparer = list.ListViewItemSorter as GuidanceItemListViewComparer;
            if (oldComparer != null)
            {
                list.ListViewItemSorter = new GuidanceItemListViewComparer(oldComparer.Columns);
            }
        }

        /// <summary>
        /// Populate the list with the provided items
        /// </summary>
        /// <param name="itemsCollection"></param>
        private void PopulateList(IGuidanceItemContainer itemsCollection, ColumnCollection columnCollection)
        {
            //Keep track of the items added to the list to avoid duplicates.
            Dictionary<string, GuidanceItem> uniqueList = new Dictionary<string, GuidanceItem>();

            //The flag to detect whether any filter has been set.
            bool autoFilterSet = false;

            //Evaluate if an autofilter has been set.
            foreach (Column column in columnCollection.Values)
            {
                autoFilterSet |= !String.IsNullOrEmpty(column.AutoFilter);
                if (autoFilterSet)
                {
                    break;
                }
            }

            //Populate the list with the items.
            foreach (GuidanceItem item in itemsCollection)
            {
                GuidanceItemFileReference fileRef = item as GuidanceItemFileReference;

                //don't include items from other libraries
                if (itemsCollection as Library != null && item.Library != itemsCollection)
                {
                    continue;
                }

                //If the item is a file reference it means it exists.
                if ((fileRef != null) &&
                    (!uniqueList.ContainsKey(fileRef.Id)) &&
                    (!autoFilterSet || EvaluateAutoFilter(fileRef, columnCollection)))
                {
                    list.Items.Add(GuidanceItemListViewItem.New(fileRef, columnCollection));
                    uniqueList.Add(fileRef.Id, fileRef);
                }
                else
                {
                    //If the guidance item is not a file reference, then it points to a file reference in a library.
                    fileRef = _modelService.Guidance.GetGuidanceItemFileReference(item);
                    if ((fileRef != null) &&
                        (!uniqueList.ContainsKey(fileRef.Id)) &&
                        (!autoFilterSet || EvaluateAutoFilter(fileRef, columnCollection)))
                    {
                        list.Items.Add(GuidanceItemListViewItem.New(fileRef, columnCollection));
                        uniqueList.Add(fileRef.Id, fileRef);
                    }
                    else
                    {
                        //Ignore to avoid duplicates.
                    }
                }
            }

            //If there are items, then select the first.
            if (list.Items.Count != 0)
            {
                list.SelectedItems.Clear();
                list.Items[0].Selected = true;
            }
            else
            {
                //There is no item selected, then clean the details view.
                if (GuidanceItemSelected != null)
                {
                    GuidanceItemEventArgs eventArgs = new GuidanceItemEventArgs(null);
                    GuidanceItemSelected(this, eventArgs);
                }
                else
                {
                    //Ignore to avoid duplicates.
                }
            }
        }

        /// <summary>
        /// Evaluates the auto filter set in the columns
        /// </summary>
        /// <param name="fileRef"></param>
        /// <param name="columnCollection"></param>
        /// <returns></returns>
        private bool EvaluateAutoFilter(GuidanceItemFileReference fileRef, ColumnCollection columnCollection)
        {
            foreach (Column column in columnCollection.Values)
            {
                //If the autofilter has been set
                if (string.IsNullOrEmpty(column.AutoFilter))
                {
                    continue;
                }

                //Compares both strings.
                if (!string.Equals(fileRef[column.Source], column.AutoFilter, StringComparison.InvariantCultureIgnoreCase))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Prepare the columns by removing the previous and creating new ones as defined in the new item container.
        /// </summary>
        /// <param name="columnCollection">The item container instance.</param>
        private void PrepareColumns(ColumnCollection columnCollection)
        {
            try
            {
                //Update the columns in the list
                list.Columns.Clear();

                SortedList<int, Column> columns = new SortedList<int, Column>(columnCollection.Count);

                foreach (Column column in columnCollection.Values)
                {
                    columns.Add(column.Position, column);
                }

                //Use default column defined in the model.
                foreach (Column column in columns.Values)
                {
                    //If the column should be enabled
                    if (column.Visible)
                    {
                        ListViewColumn listColumn = ListViewColumn.New();
                        listColumn.Name = column.Source;
                        listColumn.Text = column.Caption;
                        listColumn.Width = column.Width;
                        listColumn.Column = column;
                        list.Columns.Add(listColumn);

                        SetColumnSortIcon(listColumn);
                    }
                    else
                    {
                        //Process only visible columns.
                    }
                }
            }
            catch (Exception ex)
            {
                string message = ex.ToString();
            }
        }

        /// <summary>
        /// Publish an event if there is a single item selected in the list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void list_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (GuidanceItemSelected != null)
            {
                if (list.SelectedItems.Count == 1)
                {
                    GuidanceItemListViewItem listViewItem = list.SelectedItems[0] as GuidanceItemListViewItem;
                    if (listViewItem != null)
                    {
                        GuidanceItemEventArgs eventArgs = new GuidanceItemEventArgs(listViewItem.GuidanceItem);
                        GuidanceItemSelected(this, eventArgs);
                    }
                    else
                    {
                        //Internal bug, all items should be GuidanceItemListViewItem.
                        throw new NotImplementedException();
                    }
                }
                else
                {
                    //This event is not pushed if the selection exceeds count > 1
                }
            }
        }

        /// <summary>
        /// Occurs when an item in the list is double clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void list_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            //If a single item is selected
            if (list.SelectedItems.Count == 1)
            {
                //If the event is being monitored.
                if (GuidanceItemOpened == null)
                {
                    return;
                }

                GuidanceItemListViewItem listViewItem = list.SelectedItems[0] as GuidanceItemListViewItem;
                if (listViewItem != null)
                {
                    GuidanceItemFileReference gifr = listViewItem.GetGuidanceItemFileReference(_modelService.Guidance);
                    GuidanceItemFileReferenceEventArgs eventArgs = new GuidanceItemFileReferenceEventArgs(new GuidanceItemFileReference[] { gifr }, ReadProtection.ReadOnly);
                    GuidanceItemOpened(this, eventArgs);
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
            else
            {
                //TODO: Supports multiple edition through double click?
            }
        }

        /// <summary>
        /// Occurs when the context menu is about to be opened.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            //If the event has been subscribed.
            if (OpeningContextMenuListView != null)
            {
                OpeningContextMenuListView(sender, e);
            }
        }

        private GuidanceItemFileReference[] GetSelectedGuidanceItemFileReferences()
        {
            int count = list.SelectedItems.Count;

            if (count >= 1)
            {
                List<GuidanceItemFileReference> items = new List<GuidanceItemFileReference>(count);

                for (int i = 0; i < count; i++)
                {
                    GuidanceItemListViewItem listViewItem = list.SelectedItems[i] as GuidanceItemListViewItem;
                    if (listViewItem == null)
                    {
                        continue;
                    }
                    GuidanceItemFileReference gifr = listViewItem.GuidanceItem as GuidanceItemFileReference;

                    if (gifr != null)
                    {
                        items.Add(gifr);
                    }
                }

                return items.ToArray();
            }
            else
            {
                //If no items are selected.
            }
            return null;
        }

        /// <summary>
        /// Occurs when the item to be draged.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void list_ItemDrag(object sender, ItemDragEventArgs e)
        {
            if (list.SelectedItems.Count == 0)
            {
                throw new NotSupportedException();
                //Someone starts dragging without selecting something?
            }
            else if (list.SelectedItems.Count == 1)
            {
                GuidanceItemListViewItem listItem = e.Item as GuidanceItemListViewItem;
                if (listItem == null)
                {
                    //The list contains an item of an unknown type.
                    throw new NotImplementedException();
                }

                GuidanceItem guidanceItem = listItem.GuidanceItem as GuidanceItem;
                if (guidanceItem != null)
                {
                    list.DoDragDrop(guidanceItem, DragDropEffects.Link);
                }
                else
                {
                    //The list contains a GuidanceItem.
                    throw new NotImplementedException();
                }
            }
            else if (list.SelectedItems.Count > 1)
            {
                GuidanceItemCollection guidanceItems = GuidanceItemCollection.New();
                foreach (GuidanceItemListViewItem listItem in list.SelectedItems)
                {
                    GuidanceItem guidanceItem = listItem.GuidanceItem as GuidanceItem;
                    if (guidanceItem != null)
                    {
                        guidanceItems.Add(guidanceItem.Id, guidanceItem);
                    }
                    else
                    {
                        //The list contains a GuidanceItem.
                        throw new NotImplementedException();
                    }
                }
                list.DoDragDrop(guidanceItems, DragDropEffects.Link);
            }
        }

        /// <summary>
        /// Occurs when the column header is clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void list_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            //Get the horizontal offset of the list.
            int horizOffset = Win32.GetScrollPos(list.Handle, 0) * -1;

            //Get the key pressed state, if CTRL or SHIFT are pressed then the flag is true.
            bool keyPressed = ((Form.ModifierKeys & Keys.Control) == Keys.Control) || ((Form.ModifierKeys & Keys.Shift) == Keys.Shift);

            //Keep the position when the mouse clicked.
            Point clickPoint = MousePosition;

            //Convert the point to client coordinates
            clickPoint = PointToClient(clickPoint);

            //Get the column instance.
            ListViewColumn clickedColumn = list.Columns[e.Column] as ListViewColumn;

            //Validate jusst in case
            if (clickedColumn == null)
            {
                //The column is of an invalid type
                throw new NotSupportedException();
            }

            //Whether the image has been clicked.
            bool imageClicked = false;

            //Calculate the width up to a column, starting from the horiz scroll offset.
            int width = horizOffset;

            SortedList<int, ListViewColumn> sortedColumns = new SortedList<int, ListViewColumn>(list.Columns.Count);

            foreach (ListViewColumn column in list.Columns)
            {
                sortedColumns.Add(column.DisplayIndex, column);
            }

            //Detect if the user has clicked the special icon area.
            foreach (ListViewColumn column in sortedColumns.Values)
            {
                width += column.Width;
                if (width > clickPoint.X)
                {
                    width -= column.Width;
                    //Get the size of the text in the column. The image is at the end of the text.
                    Size columnTextSize = Graphics.FromHwnd(list.Handle).MeasureString(column.Text, list.Font, column.Column.Width - 48).ToSize();
                    if (((clickPoint.X) < (width + columnTextSize.Width + 52)) && //Some horizontal magic number to estimate the space between the text and the image.
                        ((clickPoint.X) > (width + columnTextSize.Width + 37)))
                    {
                        imageClicked = true;
                    }
                    break;
                }
            }

            //Display the list of options.
            if (imageClicked && //If the image region has been clicked
                clickedColumn.Column.Field != null && // and there is a field
                clickedColumn.Column.Field.Type == FieldType.Combo) // and the field is of type combo
            {
                //Clear the filter list.
                filterValueList.Items.Clear();

                //Populate the filter list.
                foreach (string value in GetUniqueValues(clickedColumn))
                {
                    filterValueList.Items.Add(value);
                }

                //Show the filter list
                filterValueList.Top = list.Top + 22; //Height of the header.
                filterValueList.Left = width;
                filterValueList.Visible = true;

                filterValueList.Tag = clickedColumn; //TODO: subclass the listbox and set a property.
            }
            else
            {
                //The click was outside of the filter image

                //Set the images to all the columns.
                SetColumnHeaderImagesOnSort(clickedColumn, keyPressed);
            }

            //Refresh the data.
            RefreshData();
        }

        /// <summary>
        /// Get a list with unique values in the list for a specific column.
        /// </summary>
        /// <param name="clickedColumn"></param>
        /// <returns></returns>
        private List<string> GetUniqueValues(ListViewColumn clickedColumn)
        {
            //Find the unique values.
            List<string> uniqueValues = new List<string>();

            //Iterate through all the items.
            foreach (GuidanceItem guidanceItem in _itemsContainer)
            {
                string value;
                GuidanceItemFileReference fileRef = guidanceItem as GuidanceItemFileReference;

                //If the item is a file reference it means it exists.
                if (fileRef == null)
                {
                    fileRef = _modelService.Guidance.GetGuidanceItemFileReference(guidanceItem);
                }

                value = fileRef[clickedColumn.Column.Source];

                if (!String.IsNullOrEmpty(value) &&
                    !uniqueValues.Contains(value))
                {
                    uniqueValues.Add(value);
                }
            }

            //Sort the values.
            uniqueValues.Sort();

            //Add the remove filter marker.
            uniqueValues.Insert(0, "(all)");

            return uniqueValues;
        }

        /// <summary>
        /// Set the header icons in the columns.
        /// </summary>
        /// <param name="clickedColumn"></param>
        private void SetColumnHeaderImagesOnSort(ListViewColumn clickedColumn, bool keyPressed)
        {
            //Iterate through all the columns.
            foreach (ListViewColumn column in list.Columns)
            {
                if (column != clickedColumn)
                {
                    if (!keyPressed)
                    {
                        //Set the other columns order
                        if (column.Column.Field.Type == FieldType.Combo)
                        {
                            if (String.IsNullOrEmpty(column.Column.AutoFilter)) //No auto filter set
                            {
                                column.SetImage(_headerhWnd, 3);
                            }
                            else
                            {
                                column.SetImage(_headerhWnd, 6);
                            }
                        }
                        else if (column.Column.Field.Type == FieldType.Text)
                        {
                            column.SetImage(_headerhWnd, 0);
                        }
                        else
                        {
                            //A new entry in the FieldType enum.
                            throw new NotImplementedException();
                        }
                        column.Column.Sort = SortOrder.None;
                    }
                }
                else
                {
                    //The clicked column.
                    SwitchColumnSortIcon(column);
                    SetColumnSortIcon(column);
                }
            }
        }

        /// <summary>
        /// Set the icon to the specified column.
        /// </summary>
        /// <param name="column"></param>
        private void SetColumnSortIcon(ListViewColumn column)
        {
            //Switch the column sort order.
            if (column.Column.Sort == SortOrder.Descending)
            {
                if (column.Column.Field.Type == FieldType.Combo)
                {
                    if (String.IsNullOrEmpty(column.Column.AutoFilter)) //No auto filter set
                    {
                        column.SetImage(_headerhWnd, 5);
                    }
                    else
                    {
                        column.SetImage(_headerhWnd, 8);
                    }
                }
                else if (column.Column.Field.Type == FieldType.Text)
                {
                    column.SetImage(_headerhWnd, 2);
                }
                else
                {
                    //A new entry in the FieldType enum.
                    throw new NotImplementedException();
                }
            }
            else if (column.Column.Sort == SortOrder.Ascending)
            {
                if (column.Column.Field.Type == FieldType.Combo)
                {
                    if (String.IsNullOrEmpty(column.Column.AutoFilter)) //No auto filter set
                    {
                        column.SetImage(_headerhWnd, 4);
                    }
                    else
                    {
                        column.SetImage(_headerhWnd, 7);
                    }
                }
                else if (column.Column.Field.Type == FieldType.Text)
                {
                    column.SetImage(_headerhWnd, 1);
                }
                else
                {
                    //A new entry in the FieldType enum.
                    throw new NotImplementedException();
                }
            }
            else if (column.Column.Sort == SortOrder.None)
            {
                if (column.Column.Field.Type == FieldType.Combo)
                {
                    if (String.IsNullOrEmpty(column.Column.AutoFilter)) //No auto filter set
                    {
                        column.SetImage(_headerhWnd, 3);
                    }
                    else
                    {
                        column.SetImage(_headerhWnd, 6);
                    }
                }
                else if (column.Column.Field.Type == FieldType.Text)
                {
                    column.SetImage(_headerhWnd, 0);
                }
                else
                {
                    //A new entry in the FieldType enum.
                    throw new NotImplementedException();
                }
            }
            else
            {
                //A new entry was added to the sortorder enum.
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// Switch the icon to the specified column.
        /// </summary>
        /// <param name="column"></param>
        private void SwitchColumnSortIcon(ListViewColumn column)
        {
            //Switch the column sort order.
            if ((column.Column.Sort == SortOrder.None) ||
                (column.Column.Sort == SortOrder.Descending))
            {
                column.Column.Sort = SortOrder.Ascending;
            }
            else if (column.Column.Sort == SortOrder.Ascending)
            {
                column.Column.Sort = SortOrder.Descending;
            }
            else
            {
                //A new entry was added to the sortorder enum.
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// Called when a key is pressed in the context of the list view.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void list_KeyDown(object sender, KeyEventArgs e)
        {
            if (GuidanceItemsDeleted == null)
            {
                //The event is not subscribed.
                return;
            }

            //If Delete is pressed
            if (e.KeyCode == Keys.Delete)
            {
                //If there is an item selected.
                if (list.SelectedItems.Count == 0)
                {
                    return;
                }

                GuidanceItemCollection itemCollection = GuidanceItemCollection.New();
                foreach (ListViewItem listItem in list.SelectedItems)
                {
                    GuidanceItemListViewItem guidancelistItem = listItem as GuidanceItemListViewItem;
                    if (guidancelistItem == null)
                    {
                        //The contents of the list are invalid.
                        continue;
                    }

                    //Add the item to the collection.
                    itemCollection.Add(guidancelistItem.GuidanceItem.Id, guidancelistItem.GuidanceItem);
                }

                //Call the event handlers.
                GuidanceItemsDeleted(this, new GuidanceItemCollectionEventArgs(itemCollection));
                return;
            }
            else if (e.KeyCode == Keys.Enter)
            {
                GuidanceItemOpened(this, new GuidanceItemFileReferenceEventArgs(GetSelectedGuidanceItemFileReferences(), ReadProtection.ReadOnly));
            }
            else
            {
                //Ignore other keys.
            }
        }

        /// <summary>
        /// Called when the column width has been changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void list_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
        {
            //Get the list view column instance.
            ListViewColumn listViewColumn = list.Columns[e.ColumnIndex] as ListViewColumn;
            if (listViewColumn == null)
            {
                throw new NotImplementedException();
            }

            //Get the column in the model.
            Column modelColumn = listViewColumn.Column;

            //Updapte the width.
            modelColumn.Width = listViewColumn.Width;
        }


        #region IMessageFilter Members

        /// <summary>
        /// Used to hide the filter list box when a click is received anywhere.
        /// </summary>
        /// <param name="m"></param>
        /// <returns></returns>
        public bool PreFilterMessage(ref Message m)
        {
            if ((m.Msg == 513 && m.HWnd != filterValueList.Handle) ||
                (m.Msg == 161 && m.HWnd != filterValueList.Handle) ||
                (m.Msg == 49328))
            {
                filterValueList.Visible = false;
            }
            return false;
        }

        #endregion

        /// <summary>
        /// Called when an item is selected in the filter list box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void filterValueList_SelectedIndexChanged(object sender, EventArgs e)
        {
            ListViewColumn clickedColumn = filterValueList.Tag as ListViewColumn;
            if (clickedColumn == null)
            {
                //The tag must be set.
                throw new NotImplementedException();
            }

            if (filterValueList.SelectedIndex == 0)
            {
                clickedColumn.Column.AutoFilter = null;
            }
            else
            {
                clickedColumn.Column.AutoFilter = (string)filterValueList.SelectedItem;
            }

            //Update the column icon.
            SetColumnSortIcon(clickedColumn);

            //Clear the filetr listbox.
            filterValueList.Visible = false;
            filterValueList.Tag = null;

            //Recalculate the list.
            ShowContents(_itemsContainer, _itemsContainer.Columns);
        }

        /// <summary>
        /// Called when the list columns has been reordered.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void list_ColumnReordered(object sender, ColumnReorderedEventArgs e)
        {
            int idx = 0;
            ColumnHeader[] tempColumns = new ColumnHeader[list.Columns.Count];
            foreach (ColumnHeader colh in list.Columns)
            {
                tempColumns[idx++] = colh;
            }

            Comparison<ColumnHeader> comp = delegate(ColumnHeader x, ColumnHeader y)
                {
                    return Comparer.Default.Compare(x.DisplayIndex, y.DisplayIndex);
                };
            Array.Sort<ColumnHeader>(tempColumns, comp);

            //Create a linked list to operate with the columns
            LinkedList<Column> linkedList = new LinkedList<Column>();

            //Sort the elements using the position.
            Column movedCol = null, oldCol = null;
            foreach (ColumnHeader colh in tempColumns)
            {
                foreach (Column col in _columns.Values)
                {
                    if (col.Caption == colh.Text)
                    {
                        if (colh.DisplayIndex == e.OldDisplayIndex)
                        {
                            movedCol = col;
                        }
                        if (colh.DisplayIndex == e.NewDisplayIndex)
                        {
                            oldCol = col;
                        }
                        linkedList.AddLast(col);
                        break;
                    }
                }
            }

            foreach (Column col in _columns.Values)
            {
                if (!col.Visible)
                {
                    linkedList.AddLast(col);
                }
            }

            //Change the elements in the linked list.
            linkedList.Remove(movedCol);
            if (e.OldDisplayIndex > e.NewDisplayIndex)
            {
                linkedList.AddBefore(linkedList.Find(oldCol), movedCol);
            }
            else
            {
                linkedList.AddAfter(linkedList.Find(oldCol), movedCol);
            }

            //Set the current position based on the linked list position.
            idx = 0;
            foreach (Column col in linkedList)
            {
                col.Position = idx++;
                Debug.WriteLine(col.Position + "-" + col.Caption);
            }

            //Refresh the data.
            RefreshData();
        }
    }
}

